package com.lagou.edu.factory;

import com.lagou.edu.annotation.Autowired;
import com.lagou.edu.annotation.ComponentScan;
import com.lagou.edu.annotation.Service;
import com.lagou.edu.annotation.Transactional;
import com.lagou.edu.config.SpringConfig;
import com.lagou.edu.utils.AnnotationUtils;

import java.lang.reflect.Field;
import java.util.*;

public class BeanFactory {

    /**名称单例池*/
    private static Map<String, Object> map = new HashMap<>();  // 存储对象
    /**类型单例池*/
    private static Map<String, Object> mapType = new HashMap<>();


    static {

        //解析SpringConfig
        ComponentScan scan = SpringConfig.class.getDeclaredAnnotation(ComponentScan.class);
        String[] packages = scan.value();
        Set<Class> collect = new HashSet<>();
        for (String aPackage : packages) {
            //根据包名扫描所有类，并保存到集合中
            List<Class> packageName = null;
            try {
                packageName = AnnotationUtils.getClassesByPackageName(aPackage);
            } catch (Exception e) {
                e.printStackTrace();
            }
            collect.addAll(packageName);

        }
        //遍历所有的Class
        //获取Class的所有注解
        collect.stream().forEach(c -> {
            //判断注解是否包含 @service，
            // 包含则获取该注解value值，则使用该值作为key，实例化为值保存到map中，如果value为空，则使用类名作为key。
            if (c.isAnnotationPresent(Service.class)) {
                Service service = (Service) c.getAnnotation(Service.class);
                String key = service.value();
                if ("".equals(key)) {
                    //首字母转小写
                    key = c.getSimpleName();
                    key = (new StringBuilder()).append(Character.toLowerCase(key.charAt(0))).append(key.substring(1)).toString();
                }
                String typeKey = c.getName();
                if (c.getInterfaces().length > 0) {
                    typeKey = c.getInterfaces()[0].getName();
                }
                try {
                    Object o = c.newInstance();
                    map.put(key, o);
                    mapType.put(typeKey, o);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

        //遍历整个map集合，判断是否包含@autowire。
        map.entrySet().stream().forEach(entry -> {
            Field[] declaredFields = entry.getValue().getClass().getDeclaredFields();
            for (Field field : declaredFields) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    try {
                        field.setAccessible(true);
                        //按照类型注入
                        Object val = null;
                        if (field.getType().getInterfaces().length > 0) {
                            val = mapType.get(field.getType().getInterfaces()[0].getName());
                        } else {
                            val = mapType.get(field.getType().getName());
                        }
                        //通过名称注入
                        if (val == null) {
                            val = map.get(field.getName());
                        }
                        field.set(entry.getValue(), val);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        });


        //遍历整个map，@Transaction的类需要进行动态代理增强，如果加载方法上则直接代理整个类，如果加载方法上则代理指定的方法。
        ProxyFactory proxyFactory = (ProxyFactory) map.get("proxyFactory");
        map.entrySet().stream().forEach(entry -> {
            if (entry.getValue().getClass().isAnnotationPresent(Transactional.class)) {
                //判断是否包含接口
                Object proxy;
                if (entry.getValue().getClass().getInterfaces().length > 0) {
                    proxy = proxyFactory.getJdkProxy(entry.getValue());
                } else {
                    proxy = proxyFactory.getCglibProxy(entry.getValue());
                }
                map.put(entry.getKey(), proxy);
            }
        });

    }


    public static Object getBean(String id) {
        return map.get(id);
    }

    public static Object getBean(Class clazz) {
        return mapType.get(clazz.getName());
    }

}
